﻿using Abp;
using Abp.Application.Features;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.BackgroundJobs;
using Abp.Configuration;
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Extensions;
using Abp.Runtime.Session;
using Mt.Site.Application.Dto;
using Mt.Site.Application.Navigation.Dto;
using Mt.Site.Core.BackGroundJobs;
using Mt.Site.Core.BackGroundJobs.Navigations;
using Mt.Site.Core.Base.Tree;
using Mt.Site.Core.Configuration;
using Mt.Site.Core.Navigations;
using Mt.Site.Core.Navigations.Entities;
using Mt.Site.Core.Navigations.Modules;
using Mt.Site.Core.Navigations.Permissions;
using Mt.Site.Core.Navigations.Values;
using Mt.Site.Core.Synchronization;
using Mt.Site.Core.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Mt.Site.Application.Navigation
{
    [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus)]
    public class NavigationAppService : MtSiteAppServiceBase,INavigationAppService
    {
        private readonly IMenuManager _menuManager;
        private readonly IIocResolver _iocResolver;
        private readonly ITenancyNameCache _tenancyNameCache;

        //private readonly ICommonLookupAppService _commonLookupAppService;
        //private readonly IRepository<Menu, Guid> _menuRepository;
        //private readonly IRepository<RouteSetting, Guid> _routeSettingRepository;
        //private readonly IMenuStore _menuStore;

        private readonly IModuleProvider _moduleProvider;

        private readonly IRepository<MenuConfig, Guid> _menuConfigRepository;


        private readonly IBackgroundJobManager _backgroundJobManager;

        public NavigationAppService(
            IMenuManager menuManager, 
            IIocResolver iocResolver,
            ITenancyNameCache tenancyNameCache,
            //ICommonLookupAppService commonLookupAppService,
            //IRepository<Menu, Guid> menuRepository,
            //IRepository<RouteSetting, Guid> routeSettingRepository,
            //IMenuStore menuStore,
            IModuleProvider moduleProvider,
            IRepository<MenuConfig, Guid> menuConfigRepository,
            IBackgroundJobManager backgroundJobManager)
        {
            _menuManager = menuManager;
            _iocResolver = iocResolver;
            _tenancyNameCache = tenancyNameCache;

            //_commonLookupAppService = commonLookupAppService;
            //_menuRepository = menuRepository;
            //_routeSettingRepository = routeSettingRepository;
            //_menuStore = menuStore;

            _moduleProvider = moduleProvider;
            _menuConfigRepository = menuConfigRepository;

            LocalizationSourceName = MtSiteConsts.LocalizationSourceName;

            _backgroundJobManager = backgroundJobManager;

        }

        // 读取栏目树
        public async Task<ListResultDto<MenuItemDto>> GetMenuTree()
        {
            var items = await _menuManager.GetMenuTree(AbpSession.GetTenantId(), AbpSession.CultureName());
            return new ListResultDto<MenuItemDto>(
                items.OrderBy(item=>item.Order).ToList()
                );
        }

        // 读取选项
        public async Task<DropDownSelectionsDto> GetDropDownSelectionsData(EntityDto input)
        {
            DropDownSelectionsDto dto = new DropDownSelectionsDto();

            var list = new List<NameValue<int>>();
            foreach (MenuDisplayType item in Enum.GetValues(typeof(MenuDisplayType)))
            {
                list.Add(
                    new NameValue<int>(L(EnumHelper.GetEnumDescription(item)), item.RawValue())
                    );
            }
            dto.DisplayType = list;

            var list2 = new List<NameValue<int>>();
            foreach (MenuDeviceType item in Enum.GetValues(typeof(MenuDeviceType)))
            {
                list2.Add(
                    new NameValue<int>(L(EnumHelper.GetEnumDescription(item)), item.RawValue())
                    );
            }
            dto.DeviceType = list2;

            var list4 = new List<NameValue<int>>();
            foreach (BackendMenuType item in Enum.GetValues(typeof(BackendMenuType)))
            {
                list4.Add(
                    new NameValue<int>(L(EnumHelper.GetEnumDescription(item)), item.RawValue())
                    );
            }
            dto.BackendMenuType = list4;

            var list3 = new List<NameValue<int>>();
            foreach (MenuItemGroup item in Enum.GetValues(typeof(MenuItemGroup)))
            {
                list3.Add(
                    new NameValue<int>(EnumHelper.GetEnumDescription(item), item.RawValue())
                    );
            }
            dto.ItemGroups = list3;

            using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.MayHaveTenant))
            {
                List<BackEndModule> backEndModuleList = await _menuManager.GetBackEndModuleList();
                List<FrontEndPage> frontEndPageList = await _menuManager.GetFrontEndPageList();

                using (var featureDependencyContext = _iocResolver.ResolveAsDisposable<FeatureDependencyContext>())
                {
                    featureDependencyContext.Object.TenantId = input.Id;

                    dto.BackEndModule = backEndModuleList
                        .Where(c =>
                        {
                            IFeatureDependency featureDependency = _moduleProvider.TryGetFeatureDependencyByModuleName(c.Name);
                            
                            if (featureDependency != null)
                            {
                                return featureDependency.IsSatisfied(featureDependencyContext.Object);
                            }
                            return true;
                        })
                        .OrderBy(c => c.Order)
                        .Select(
                            c => new NameValueChild
                            {
                                Name = c.DisplayName,
                                Value = c.Id.ToString(),
                                Child = frontEndPageList.Where(f => f.BackEndModuleId == c.Id && f.IsDetailPage == false)
                                    .OrderBy(f => f.Order)
                                    .Select(f => new NameValue(f.Name, f.Id.ToString())).ToList()
                            }
                       ).ToList();
                }

                dto.BackEndModule.Insert(0, new NameValueChild {
                    Name = L("UnSelect"),
                    Value = Guid.Empty.ToString(),
                    Child = frontEndPageList.Where(f => f.BackEndModuleId == Guid.Empty && f.IsDetailPage == false)
                                    .OrderBy(f => f.Order)
                                    .Select(f => new NameValue(f.Name, f.Id.ToString())).ToList()
                });
            }

            //是否存在多个语种
            dto.Local = (await _menuManager.GetLanguageCount(AbpSession.GetTenantId()) > 1 )
                || (await SettingManager.GetSettingValueForTenantAsync<bool>(AppSettings.Synchronous.DataSynchronous, AbpSession.GetTenantId()));
            return dto;
        }
        public async Task<MenuItemDto> GetMenuItem(EntityDto<Guid> input)
        {
            return await GetMenuItemDto(input.Id);
        }


        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Create)]
        public async Task<MenuItemDto> CreateMenuItem(CreateMenuItemInput input)
        {
            var menu = ObjectMapper.Map<Menu>(input);
            menu.Code = await _menuManager.GetNextChildCodeAsync(menu.ParentId);
            menu.Order = int.Parse(CodeHelper.GetLastUnitCode(menu.Code));
            menu.Id = Guid.NewGuid();

            await _menuManager.CreateAsync(menu);

            await CurrentUnitOfWork.SaveChangesAsync();

            var dto = await GetMenuItemDto(menu.Id);
            dto.LocalDisplayName = input.LocalDisplayName;
            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());
            return dto;
        }

        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Edit)]
        public async Task<MenuItemDto> UpdateMenuItem(UpdateMenuItemInput input)
        {
            var menu = await _menuManager.GetAsync(input.Id);
            //20211125暂时注释，时间时区被转化
            //AppCheck.CheckLastModificationTime(input.LastModificationTime, menu, L("DataHasBeenChanged"));

            ObjectMapper.Map(input, menu);

            await _menuManager.UpdateAsync(menu);
            await CurrentUnitOfWork.SaveChangesAsync();

            if (MtSiteConsts2.BackgroundJobs)
            {
                _backgroundJobManager.Enqueue<SynchorizationMenuJob, BaseAndMenuIdArgs>(
                new BaseAndMenuIdArgs
                {
                    FromLanguage = AbpSession.CultureName(),
                    Id = menu.Id,
                    FromTenantId = AbpSession.GetTenantId(),
                    MenuId = menu.AuxiliaryId,
                    OperateType = OptionType.Update
                });
            }


            var dto = await GetMenuItemDto(menu.Id);
            dto.LocalDisplayName = menu.LocalDisplayName;
            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());
            return dto;
        }

        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Move)]
        public async Task MoveMenuItem(MoveMenuItemInput input)
        {
            await _menuManager.MoveAsync(input.Id, input.NewParentId);

            await CurrentUnitOfWork.SaveChangesAsync();
            if (MtSiteConsts2.BackgroundJobs)
            {
                _backgroundJobManager.Enqueue<SynchorizationMenuJob, BaseAndMenuIdArgs>(
                new BaseAndMenuIdArgs
                {
                    FromLanguage = AbpSession.CultureName(),
                    Id = input.Id,
                    FromTenantId = AbpSession.GetTenantId(),
                    OperateType = OptionType.Update
                });
            }
            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());
        }

        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Delete)]
        public async Task DeleteMenuItem(EntityDto<Guid> input)
        {
            await _menuManager.DeleteAsync(input.Id, AbpSession.GetTenantId(), AbpSession.CultureName());
            await CurrentUnitOfWork.SaveChangesAsync();
            if (MtSiteConsts2.BackgroundJobs)
            {
                _backgroundJobManager.Enqueue<SynchorizationMenuJob, BaseAndMenuIdArgs>(
                 new BaseAndMenuIdArgs
                 {
                     FromLanguage = AbpSession.CultureName(),
                     Id = input.Id,
                     FromTenantId = AbpSession.GetTenantId(),
                     OperateType = OptionType.Delete
                 });
            }
            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());
        }

        // 顺序 上移下移
        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Edit)]
        public async Task<ListResultDto<MenuItemDto>> UpSort(EntityDto<Guid> input)
        {
            List<Guid> list = await _menuManager.ChangeSort(input.Id, true);
            await CurrentUnitOfWork.SaveChangesAsync();
            var result = new List<MenuItemDto>();
            foreach (var item in list)
            {
                result.Add(await GetMenuItemDto(item));
            }

            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());

            return new ListResultDto<MenuItemDto>(result);
        }

        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Edit)]
        public async Task<ListResultDto<MenuItemDto>> DownSort(EntityDto<Guid> input)
        {
            List<Guid> list = await _menuManager.ChangeSort(input.Id, false);
            await CurrentUnitOfWork.SaveChangesAsync();
            var result = new List<MenuItemDto>();
            foreach (var item in list)
            {
                result.Add(await GetMenuItemDto(item));
            }

            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());

            return new ListResultDto<MenuItemDto>(result);
        }

        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Edit)]
        public async Task<MenuItemDto> ChangeSortNum(NewSortNumberInput input)
        {
            var menu = await _menuManager.GetAsync(input.Id);
            menu.Order = input.NewNumber;      //Order与Code最后节点，不再强求一致。
            await _menuManager.UpdateAsync(menu);
            await CurrentUnitOfWork.SaveChangesAsync();

            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());

            return await GetMenuItemDto(input.Id);
        }

        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Edit)]
        public async Task<MenuItemDto> SetActive(EntityDto<Guid> input)
        {
            var menu = await _menuManager.GetAsync(input.Id);
            menu.IsActive = true;
            await _menuManager.UpdateAsync(menu);
            await CurrentUnitOfWork.SaveChangesAsync();

            if (MtSiteConsts2.BackgroundJobs)
            {
                _backgroundJobManager.Enqueue<SynchorizationMenuJob, BaseAndMenuIdArgs>(
                 new BaseAndMenuIdArgs
                 {
                     FromLanguage = AbpSession.CultureName(),
                     Id = input.Id,
                     FromTenantId = AbpSession.GetTenantId(),
                     OperateType = OptionType.Update
                 });
            }
            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());

            return await GetMenuItemDto(input.Id);
        }

        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Edit)]
        public async Task<MenuItemDto> SetInActive(EntityDto<Guid> input)
        {
            var menu = await _menuManager.GetAsync(input.Id);
            menu.IsActive = false;
            await _menuManager.UpdateAsync(menu);
            await CurrentUnitOfWork.SaveChangesAsync();

            if (MtSiteConsts2.BackgroundJobs)
            {
                _backgroundJobManager.Enqueue<SynchorizationMenuJob, BaseAndMenuIdArgs>(
                 new BaseAndMenuIdArgs
                 {
                     FromLanguage = AbpSession.CultureName(),
                     Id = input.Id,
                     FromTenantId = AbpSession.GetTenantId(),
                     OperateType = OptionType.Update
                 });
            }
            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());

            return await GetMenuItemDto(input.Id);
        }

        public async Task<StyleAndScriptDto> GetStyleAndScriptConfigAsync(MenuGuidDto input)
        {
            var b = await _menuManager.GetStyleAndScriptConfigAsync(input.MenuId);
            if(b == null)
            {
                return new StyleAndScriptDto { MenuId = input.MenuId };
            }
            else
            {
                return ObjectMapper.Map<StyleAndScriptDto>(b);
            }
        }

        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_StyleAndScript)]
        public async Task SetStyleAndScriptConfigAsync(StyleAndScriptConfigInput input)
        {
            var b = ObjectMapper.Map<Bundling>(input);
            await _menuManager.SetStyleAndScriptConfigAsync(b);

            await CurrentUnitOfWork.SaveChangesAsync();

            if (MtSiteConsts2.BackgroundJobs)
            {
                _backgroundJobManager.Enqueue<SynchorizationStylesJob, BaseAndMenuIdArgs>(
                 new BaseAndMenuIdArgs
                 {
                     FromLanguage = AbpSession.CultureName(),
                     Id = input.MenuId,
                     FromTenantId = AbpSession.GetTenantId(),
                     OperateType = OptionType.Update
                 });
            }
            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());
        }


        public string TranslateToUrl(List<string> pathNames)
        {
            var cultureName = AbpSession.CultureName();
            if (cultureName == "en" || cultureName.StartsWith("en"))
            {
                string str = _menuManager.TranslateToUrl(pathNames);
                var tenancyName = _tenancyNameCache.TenancyName;
                return $"/{tenancyName}/{cultureName}/{str}";
            }
            return "";
        }

        private async Task<MenuItemDto> GetMenuItemDto(Guid menuId)
        {
            //using (var uow = UnitOfWorkManager.Begin(new UnitOfWorkOptions
            //{
            //    Scope = TransactionScopeOption.RequiresNew,
            //    IsTransactional = false,
            //    IsolationLevel = IsolationLevel.ReadUncommitted
            //}))
            //{
                var menu = await _menuManager.GetAsync(menuId);
                return ObjectMapper.Map<MenuItemDto>(await _menuManager.AdapterMenuItem(menu));
            //}
        }

        #region 批量生成栏目路由

        /// <summary>
        /// 生成路由规则： 1.已发布 2.有前台页面(非外部链接) 3. 栏目在前台导航显示 4. 尚未配置路由
        ///     5. 查找母站是否有相同栏目，有则同步，没有且当前语种为en时自行生成
        /// </summary>
        /// <returns></returns>
        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Edit)]
        public async Task<GenerateRouteOutput> RouteBatchGeneration()
        {
            var list = await _menuManager.FindTheMenuItemsForGeneratingRoute();
            var tenantId = AbpSession.GetTenantId();

            var hadGenerated = 0;
            var canNotGenerated = new List<MenuItemSimpleInfo>();
            foreach (var item in list)
            {
                var r = await _menuManager.GenerateRouteByMenuId(item, tenantId);
                if (r)
                {
                    hadGenerated++;
                }
                else
                {
                    canNotGenerated.Add(item);
                }
            }
            return new GenerateRouteOutput
            {
                CanNotGenerated = canNotGenerated,
                HadGenerated = hadGenerated
            };
        }

        [AbpAuthorize(MenuPermissions.Pages_Tenant_Menus_Copy)]
        public async Task<MenuItemDto> CopyMenuAsync(EntityDto<Guid> input)
        {
            var menu = await _menuManager.GetAsync(input.Id);

            Menu model = new Menu();
            model.Id = Guid.NewGuid();
            model.DisplayName = menu.DisplayName + DateTime.Now.ToString("HHmmssfff");
            model.Code = await _menuManager.GetNextChildCodeAsync(menu.ParentId);
            model.Order = int.Parse(CodeHelper.GetLastUnitCode(model.Code));
            model.AutoSynchronous = menu.AutoSynchronous;
            model.AutoSynchronousForContent = menu.AutoSynchronousForContent;
            model.BackEndModuleId = menu.BackEndModuleId;
            model.ChannelRecommendation = menu.ChannelRecommendation;
            model.CreationTime = menu.CreationTime;
            model.CreatorUserId = menu.CreatorUserId;
            model.CustomData = menu.CustomData;
            model.CustomUrl = menu.CustomUrl;
            model.DeviceType = menu.DeviceType;
            model.DisplayType = menu.DisplayType;
            model.BackendMenuType = menu.BackendMenuType;
            model.FrontEndPageId = menu.FrontEndPageId;
            model.HomeRecommendation = menu.HomeRecommendation;
            model.BackendIcon = menu.BackendIcon;
            model.IsActive = menu.IsActive;
            model.IsDeleted = menu.IsDeleted;
            model.IsNavigation4FrontEnd = menu.IsNavigation4FrontEnd;
            model.IsSpecial = false;
            model.SourceID = menu.SourceID;
            model.TenantId = menu.TenantId;
            model.MenuItemGroups = menu.MenuItemGroups;
            model.ParentId = menu.ParentId;
            await _menuManager.CreateAsync(model);
            await CurrentUnitOfWork.SaveChangesAsync();

            var list = _menuConfigRepository.GetAll().Where(t => t.MenuId == menu.AuxiliaryId);

            if (list != null)
            {
                foreach (var item in list)
                {
                    MenuConfig mmodel = new MenuConfig();
                    mmodel.MenuId = model.AuxiliaryId;
                    mmodel.Type = item.Type;
                    mmodel.ExtensionData = item.ExtensionData;
                    mmodel.AutoSynchronous = item.AutoSynchronous;
                    mmodel.IsDeleted = item.IsDeleted;
                    _menuConfigRepository.Insert(mmodel);
                }
            }

            if (MtSiteConsts2.BackgroundJobs)
            {
                _backgroundJobManager.Enqueue<SynchorizationMenuJob, BaseAndMenuIdArgs>(
                 new BaseAndMenuIdArgs
                 {
                     FromLanguage = AbpSession.CultureName(),
                     Id = input.Id,
                     FromTenantId = AbpSession.GetTenantId(),
                     OperateType = OptionType.Update
                 });
            }

            await _menuManager.ResetMenuCacheAsync(AbpSession.GetTenantId());


            var dto = await GetMenuItemDto(model.Id);
            dto.LocalDisplayName = model.LocalDisplayName;
            return dto;
        }

        #endregion
    }
}
